/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
 */

package com.device.cp.penspeeddemo;

import java.util.concurrent.ConcurrentHashMap;

import android.annotation.SuppressLint;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import static android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY;
import static android.os.Process.setThreadPriority;

/**
 * MainActivity, 画布窗口页面
 *
 * @since 2022-01-18
 */
public class MainActivity extends AppCompatActivity {
    private static final int CANVAS_COLOR = 0xFF123D36;
    private static final int PEN_COLOR_DRAWING = 0xFF5BD2F6;
    private static final float PEN_WIDTH_DRAWING = 2.8f;
    private static final int PEN_COLOR_IN_CANVAS = PEN_COLOR_DRAWING;
    private static final float PEN_WIDTH_IN_CANVAS = PEN_WIDTH_DRAWING;
    // private static final float PEN_WIDTH_IN_CANVAS = 0xFF00FF00; // 绿色
    private static final int FAST_PEN_COLOR = PEN_COLOR_DRAWING;
    // private static final int FAST_PEN_COLOR = 0xFFFF0000; // 红色
    private static final float FAST_PEN_WIDTH = PEN_WIDTH_DRAWING * 0.5f;
    private static boolean IS_USE_FASTPEN = true; // 是否使用penspeed加速
    private static final boolean IS_USE_GET_POINTER_WAY = false; // 是否使用分段get点的方式

    private static final String TAG = "PenSpeedDemo";

    private static Handler mRenderHandler;

    static {
        HandlerThread workThread = new HandlerThread("MyRenderThread");
        workThread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(@NonNull Thread thread, @NonNull Throwable exception) {
                Log.e(Thread.currentThread().getName(), "UncaughtException happen !!!", exception);
                HandlerThread workThread = new HandlerThread("MyRenderThread");
                workThread.setUncaughtExceptionHandler(this);
                workThread.setPriority(Thread.MAX_PRIORITY);
                workThread.start();
                mRenderHandler = new Handler(workThread.getLooper());
                mRenderHandler.post(() -> setThreadPriority(THREAD_PRIORITY_URGENT_DISPLAY));
            }
        });
        workThread.setPriority(Thread.MAX_PRIORITY);
        workThread.start();
        mRenderHandler = new Handler(workThread.getLooper());
        mRenderHandler.post(() -> setThreadPriority(THREAD_PRIORITY_URGENT_DISPLAY));
    }

    private final ConcurrentHashMap<Path, Path> mFinishedPaths = new ConcurrentHashMap<>();

    private final ConcurrentHashMap<Integer, Path> mFastPaths = new ConcurrentHashMap<>();

    @SuppressLint("ClickableViewAccessibility")
    private final View.OnTouchListener mOnTouchListener = (view, event) -> {
        PointF ptCur = new PointF();
        int curPointerId = event.getPointerId(event.getActionIndex());
        int curPointerIndex = event.findPointerIndex(curPointerId);
        ptCur.set(event.getX(curPointerIndex), event.getY(curPointerIndex));
        int pointerCount = event.getPointerCount();
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            // 单指落下
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_POINTER_DOWN: {
                handleDown(ptCur, curPointerId);
                break;
            }
            // 单指抬起
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                handleUp(event, pointerCount);
                break;
            case MotionEvent.ACTION_POINTER_UP: {
                handlePointerUp(curPointerId);
                break;
            }
            // 移动过程中
            case MotionEvent.ACTION_MOVE: {
                handleMove(event, pointerCount);
                break;
            }
            default:
                break;
        }
        return true;
    };

    private SurfaceHolder holder;

    private SurfaceView surface;

    private PenSpeedProxy penSpeedProxy;

    /**
     * fastpen java回调函数处理函数
     *
     * @param pointId 触点Id
     * @param posXInView 相对于view坐标系下的x轴坐标
     * @param posYInView 相对于view坐标系下的y轴坐标
     */
    public void fastPenOnTouch(int pointId, float posXInView, float posYInView) {
        if (pointId != -1 && mFastPaths.containsKey(pointId)) {
            Path drawingPath = mFastPaths.get(pointId);
            if (drawingPath != null) {
                if (drawingPath.isEmpty()) {
                    drawingPath.moveTo(posXInView, posYInView);
                }
                drawingPath.lineTo(posXInView, posYInView);
            }
        }
        mRenderHandler.post(() -> {
            Canvas canvas = holder.lockCanvas();
            if (canvas == null) {
                return;
            }
            canvas.drawColor(CANVAS_COLOR);
            Paint paint = new Paint();
            paint.setStrokeJoin(Paint.Join.ROUND);
            paint.setStrokeCap(Paint.Cap.ROUND);
            paint.setStyle(Paint.Style.STROKE);
            paint.setColor(PEN_COLOR_DRAWING);
            paint.setStrokeWidth(PEN_WIDTH_DRAWING);
            paint.setAntiAlias(true);

            mFastPaths.forEach((id, path) -> canvas.drawPath(path, paint));
            paint.setColor(PEN_COLOR_IN_CANVAS);
            paint.setStrokeWidth(PEN_WIDTH_IN_CANVAS);
            mFinishedPaths.forEach((path, value) -> canvas.drawPath(path, paint));
            holder.unlockCanvasAndPost(canvas);
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.i(TAG, "onStart");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.i(TAG, "onStop");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mRenderHandler.removeCallbacksAndMessages(null);
        Log.i(TAG, "onDestroy");
    }

    @Override
    protected void onPostResume() {
        super.onPostResume();
        fastPenOnTouch(-1, 0, 0);
        Log.i(TAG, "onPostResume");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.i(TAG, "onResume");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.i(TAG, "onRestart");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.i(TAG, "onPause");
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(TAG, "onCreate");

        setContentView(R.layout.activity_main);
        surface = findViewById(R.id.drawPanel);
        // 初始化SurfaceHolder对象
        holder = surface.getHolder();

        holder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                Rect viewRect = UiUtils.getViewRectInScreen(surface);
                Log.i(TAG, "surfaceCreated surfaceView in screen rect：" + viewRect);
                if (IS_USE_FASTPEN) {
                    penSpeedProxy = new PenSpeedProxy(MainActivity.this);
                    if (!penSpeedProxy.isFastPenOk) {
                        IS_USE_FASTPEN = false;
                        penSpeedProxy.release();
                        penSpeedProxy = null;
                        return;
                    }
                    penSpeedProxy.nativeSetGetPointerWay(IS_USE_GET_POINTER_WAY);
                    penSpeedProxy.nativeSetFastPenColorWidth(FAST_PEN_COLOR, FAST_PEN_WIDTH);
                    penSpeedProxy.fbCreateCanvas(viewRect.left, viewRect.top, viewRect.right, viewRect.bottom);
                }
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                Canvas canvas = holder.lockCanvas();
                canvas.drawColor(CANVAS_COLOR);
                Rect viewRect = UiUtils.getViewRectInScreen(surface);
                Log.i(TAG, "surfaceChanged surfaceView in screen rect：" + viewRect);
                if (IS_USE_FASTPEN) {
                    penSpeedProxy.fbDeleteCanvas();
                    penSpeedProxy.fbCreateCanvas(viewRect.left, viewRect.top, viewRect.right, viewRect.bottom);
                }
                holder.unlockCanvasAndPost(canvas);
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                Log.i(TAG, "surfaceDestroyed");
                if (IS_USE_FASTPEN) {
                    penSpeedProxy.fbDeleteCanvas();
                    penSpeedProxy.release();
                    penSpeedProxy = null;
                }
            }
        });

        surface.setOnTouchListener(mOnTouchListener);
    }

    private void handleMove(MotionEvent event, int pointerCount) {
        for (int i = 0; i < pointerCount; i++) {
            if (IS_USE_FASTPEN) {
                if (IS_USE_GET_POINTER_WAY) {
                    penSpeedProxy.onMove(event.getPointerId(i));
                }
            } else {
                fastPenOnTouch(event.getPointerId(i), event.getX(i), event.getY(i));
            }
        }
    }

    private void handlePointerUp(int curPointerId) {
        Path path = mFastPaths.getOrDefault(curPointerId, null);
        if (path != null) {
            mFinishedPaths.put(path, path);
            mFastPaths.remove(curPointerId);
        }
        if (IS_USE_FASTPEN) {
            penSpeedProxy.onUp(curPointerId);
        }
    }

    private void handleUp(MotionEvent event, int pointerCount) {
        for (int i = 0; i < pointerCount; i++) {
            int id = event.getPointerId(i);
            if (IS_USE_FASTPEN) {
                penSpeedProxy.onUp(id);
            }
            Path path = mFastPaths.getOrDefault(id, null);
            if (path != null) {
                mFinishedPaths.put(path, path);
                mFastPaths.remove(id);
            }
        }
    }

    private void handleDown(PointF ptCur, int curPointerId) {
        Path path = mFastPaths.getOrDefault(curPointerId, new Path());
        path.reset();
        mFastPaths.put(curPointerId, path);
        if (IS_USE_FASTPEN) {
            penSpeedProxy.onDown(curPointerId, ptCur.x, ptCur.y);
        }
    }
}